//
// Created by taojiaheng on 2020/5/27.
//

#include "Socket.h"
#include "../Timer/Timer.h"
Socket::Socket() {
    // 初始化工作
    message_handler = NULL;
    connect_handler = NULL;
    close_handler = NULL;
    protocol = NULL;
    eventHandler = NULL;
    protocol_connect_handler = NULL;
    connection_list = new std::unordered_map<long long, Connection *>();
    ping_interval_task = NULL;
}

Socket::~Socket() {
    if(ping_interval_task != NULL){
        // 停止定时任务
        ping_interval_task->timer_exec_count = 0;
    }
}

void Socket::initServer(const char * ip, int port) {
    is_server = true;
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if(server_fd == -1) {
        printf("创建服务sock失败\n");
        exit(1);
    }
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    // 设置监听的IP
    inet_pton(AF_INET, ip, &server_addr.sin_addr);
    // 绑定端口
    server_addr.sin_port = htons(port);
    int is_bind = bind(server_fd, (sockaddr *)&server_addr, sizeof(server_addr));
    if(is_bind == -1) {
        printf("绑定端口失败\n");
        exit(-2);
    }
    // 设置最大文件数
    struct rlimit rt;
    rt.rlim_max = rt.rlim_cur = 65535;
    if (setrlimit(RLIMIT_NOFILE, &rt) == -1)
    {
        perror("setrlimit error");
        exit(-100);
    }
    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    listen(server_fd, SOCKET_Q);
    int flags = fcntl(server_fd, F_GETFL, 0);
    flags = flags | O_NONBLOCK;
    fcntl(server_fd, F_SETFL, flags);
}

void Socket::runServer() {
    // 检测心跳
    connectionPingInterval();
    epoll_event events[EPOLL_EVENTS];
    int ret;
    // 创建epoll
    epoll_fd = epoll_create(EPOLL_FD_SIZE);
    // 添加监听服务套接字事件
    Connection * serverClient = new Connection(server_fd, server_addr);
    addEvent(server_fd, EPOLLIN, &(serverClient->client_id));
    serverClient->server_sock = this;
    connection_list->insert(std::pair<long long, Connection*>(serverClient->client_id, serverClient));
    while (true) {
        ret = epoll_wait(epoll_fd, events, EPOLL_EVENTS, -1);
        handleEvents(events, ret);
    }
}

/**
 * epoll事件分发
 * @param events 需要处理的事件队列
 * @param num 事件队列的大小
 */
void Socket::handleEvents(epoll_event *events, int num) {
    int fd;
    for(int i = 0; i < num; i++) {
        Connection* client = getConnectionFormList((long long *)(events[i].data.ptr));
        fd = client->fd;
        // 根据事件类型分发
        if((fd == server_fd) && (events[i].events & EPOLLIN)){
            // ACCEPT 接受链接
            doAccept();
        }else if(events[i].events & EPOLLIN){
            // READER 读取数据
            doRead((long long *)(events[i].data.ptr));
        }else if(events[i].events & EPOLLOUT){
            // WRITE 发送数据 pass
        }else if(events[i].events & EPOLLHUP){
            // 有链接被挂断 EPOLLHUP
            closeClient(client);
        }else if(events[i].events & EPOLLERR) {
            // 有链接发生错误
            closeClient(client);
        }else if (events[i].events & EPOLLRDHUP) {
            // 有链接被挂断 EPOLLRDHUP
            closeClient(client);
        }
    }
}

/**
 * 处理接受事件
 */
void Socket::doAccept() {
    int client_fd;
    struct sockaddr_in client_addr = {0};
    socklen_t client_addr_len = {0};
    bzero(&client_addr, sizeof(client_addr));
    client_addr_len = sizeof(client_addr);
    client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_addr_len);
    if(client_fd == -1) {
       // perror("接受请求时出错\n");
    }else{
        // 将链接信息加入EPOLL自定义指针
        Connection * sockClient = new Connection(client_fd, client_addr);
        sockClient->server_sock = this;
        // 将链接信息加入链接集合
        connection_list->insert(std::pair<long long, Connection*>(sockClient->client_id, sockClient));
        addEvent(client_fd, EPOLLIN, &(sockClient->client_id));
        triggerConnectHandler(sockClient);
    }
}

/**
 * 添加一个套接字到epoll监听队列
 * @param fd
 * @param state
 */
int Socket::addEvent(int fd, int state, void * ptr) {
    epoll_event ev;
    ev.events = state;
    ev.data.fd = fd;
    ev.data.ptr = ptr;
    return epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
}

/**
 * 删除一个套接字到epoll监听队列
 * @param fd
 * @param state
 */
void Socket::deleteEvent(int fd, int state) {
    epoll_event ev;
    ev.events = state;
    ev.data.fd = fd;
    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &ev);
}

/**
 * 修改一个套接字到epoll到监听队列
 * @param fd
 * @param state
 */
void Socket::modifyEvent(int fd, int state) {
    epoll_event ev;
    ev.events = state;
    ev.data.fd = fd;
    epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ev);
}

void Socket::modifyEvent(int fd, int state, void * data) {
    epoll_event ev;
    ev.events = state;
    ev.data.ptr = data;
    epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ev);
}

void Socket::doRead(long long * client_id_ptr) {
    char * buff = new char[MAX_SIZE + 1];
    memset(buff, 0, MAX_SIZE + 1);
    Connection * client = getConnectionFormList(client_id_ptr);
    if(client == NULL) {
        delete [] buff;
        return;
    }
    int fd = client->fd;
    int read_len = recv(fd, buff, MAX_SIZE, MSG_DONTWAIT);
    if(read_len == -1)
    {
        closeClient(client);
        delete []buff;
    }else if(read_len == 0){
        closeClient(client);
        delete []buff;
    }else{
        client->last_session_time = time();
        // 组装recv对象
        BUFF * recv_buff = new BUFF;
        recv_buff->buff = buff;
        recv_buff->buff_len = read_len;
        // 将数据添加到缓冲区
        client->conn_buffer->insetBuff(recv_buff);
        BUFF * connectBuffer = client->conn_buffer->toBuff();
        // 检查协议是否需要握手如果需要握手处理握手逻辑
        if(protocol->is_handshake && !client->is_handshake){
            // 需要握手
            int handshake_status = protocol->handshake(connectBuffer, client);
            if(handshake_status == -1) {
                DEL_BUFF(recv_buff); delete connectBuffer;
                return;
            }else if(handshake_status == -2){
                // 删除buff
                client->conn_buffer->clear();
                DEL_BUFF(recv_buff); delete connectBuffer;
                return;
            }else if(handshake_status == 0) {
                // 握手失败关闭缓冲区
                closeClient(client);
                DEL_BUFF(recv_buff); delete connectBuffer;
                return;
            }else if(handshake_status == 1){
                // 握手成功
                triggerProtocolConnectHandler(client);
                if (checkClientIdIsExist(*client_id_ptr))
                    client->conn_buffer->clear();
                delete connectBuffer; DEL_BUFF(recv_buff);
            }
        }
        // 分包
        while (true) {
            if(!checkClientIdIsExist(*client_id_ptr)){
                DEL_BUFF(recv_buff); delete connectBuffer;
                return;
            }
            if(client->current_package_size > 0) { // 已经知道数据包大小
                if(client->conn_buffer->length() >= client->current_package_size){ // 缓冲区长度大于数据包大小 可以进行解包
                    BUFF * sub_buff = client->conn_buffer->popBuff(0, client->current_package_size);
                    BUFF * response_data = protocol->decode(sub_buff, client);
                    client->current_package_size = 0;
                    triggerMessageHandler(client, response_data);
                    DEL_BUFF(response_data); DEL_BUFF(sub_buff);
                }else{ // 数据包长度不够
                    DEL_BUFF(recv_buff); delete connectBuffer;
                    break;
                }
            }else{
                int input_package_len = protocol->input(connectBuffer, client);
                if(input_package_len == 0) { // 没有检测出包的大小略过
                    DEL_BUFF(recv_buff); delete connectBuffer;
                    break;
                }else{
                    if(client->conn_buffer->length() < input_package_len){
                        DEL_BUFF(recv_buff); delete connectBuffer;
                        break;
                    }else{
                        //解包
                        client->current_package_size = input_package_len;
                        BUFF * sub_buff = client->conn_buffer->popBuff(0, client->current_package_size);
                        BUFF * response_data = protocol->decode(sub_buff, client);
                        client->current_package_size = 0;
                        triggerMessageHandler(client, response_data);
                        DEL_BUFF(response_data); DEL_BUFF(sub_buff);
                    }
                }
            }
        }
    }
}

/**
 * 设置链接接受数据时触发的回调
 * @param _message_handler 回调的函数指针
 */
void Socket::setMessageHandler(void (*_message_handler)(Connection *, BUFF *)) {
    message_handler = _message_handler;
}

/**
 * 设置当链接 链接 时的回调
 * @param _connect_handler 回调函数指针
 */
void Socket::setConnectHandler(void (*_connect_handler)(Connection *)) {
    connect_handler = _connect_handler;
}

/**
 * 设置当链接关闭时的回调
 * @param _close_handler 回调函数指针
 */
void Socket::setCloseHandler(void (*_close_handler)(Connection *)) {
    close_handler = _close_handler;
}

/**
 * 根据 sockaddr_in 计算链接唯一ID
 * @param addr 链接地址信息
 * @return ID
 */
unsigned long long Socket::getUnionId(sockaddr_in *addr) {
    unsigned long long id = (unsigned long long)addr->sin_addr.s_addr * 100000 + addr->sin_port;
    return id;
}

void Socket::closeClient(Connection * client) {
    int closed = close(client->fd);
    if(closed == -1) {
        perror("关闭链接失败\n");
    }
    deleteEvent(client->fd, EPOLLIN);
	removeConnectionFormList(client->client_id);
    triggerCloseHandler(client);
    delete client;
}

/**
 * 设置协议
 */
void Socket::setProtocol(Protocol * _protocol) {
    protocol = _protocol;
}

void Socket::triggerConnectHandler(Connection * sockClient) {
    if(connect_handler != NULL) {
        connect_handler(sockClient);
        return;
    }
    if(eventHandler != NULL) {
        eventHandler->onConnect(sockClient);
        return;
    }
}

void Socket::triggerMessageHandler(Connection *client, BUFF * buff) {
    if(message_handler != NULL) {
        message_handler(client, buff);
        return;
    }
    if(eventHandler != NULL) {
        eventHandler->onMessage(client, buff);
        return;
    }
}

void Socket::triggerCloseHandler(Connection * client) {
    if(close_handler != NULL) {
        close_handler(client);
        return;
    }
    if(eventHandler != NULL) {
        eventHandler->onClose(client);
        return;
    }
}

void Socket::triggerProtocolConnectHandler(Connection * client) {
    if(protocol_connect_handler != NULL) {
        protocol_connect_handler(client);
        return;
    }
    if(eventHandler != NULL) {
        eventHandler->onProtocolConnect(client);
    }

}

void Socket::initConnectPoll() {
    is_server = false;
    epoll_fd = epoll_create(EPOLL_FD_SIZE);
}

long long Socket::connectServer(const char *ip, int port) {
    // 初始化连接到客户端套接字
    struct sockaddr_in server_addr;
    int client_fd = socket(AF_INET, SOCK_STREAM, 0);
    if(client_fd < 0) {
        printf("链接到服务器时初始化套接字失败\n");
        return NULL;
    }
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    if(inet_pton(AF_INET, ip, &server_addr.sin_addr) < 0){
        printf("连接到服务器时设置链接地址时失败\n");
        return NULL;
    }
    // 将套接字存放到epoll下管理
    // 建立链接对象
    Connection * client = new Connection(client_fd, server_addr);
    client->server_sock = this;
    connection_list->insert(std::pair<long long, Connection*>(client->client_id, client));
    if(addEvent(client_fd, EPOLLOUT, &(client->client_id)) == -1) {
        printf("添加到 epoll失败\n");
    }
    // 异步链接服务器
    int val = 1;
    ioctl(client_fd, FIONBIO, &val);
    int ret = connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    return client->client_id;
}

void Socket::runConnect(bool is_new_thread) {
    // 监听 epoll
    // 定义一个秘名函数
    auto fun = [](void * data) -> void *{
        Socket * sock = (Socket *) data;
        epoll_event events[EPOLL_EVENTS];
        while (true){
            int num = epoll_wait(sock->epoll_fd, events, EPOLL_EVENTS, -1);
            sock->connectEventHandler(events, num);
        }
        return nullptr;
    };
    if(is_new_thread == false){
        fun(this);
    }else{
        // 新的线程中运行服务器
        pthread_t t;
        int ret = pthread_create(&t, NULL, fun, this);
        if(ret != 0) {
            printf("线程创建失败\n");
        }
    }
}

void Socket::connectEventHandler(epoll_event *events, int num) {
    for(int i = 0; i < num; i++) {
        Connection* client = getConnectionFormList((long long *)events[i].data.ptr);
        if(client == NULL) continue;
        // 根据事件类型分发
        if(events[i].events & EPOLLOUT){
            // 链接成功
            // WRITE 发送数据 pass
            client->is_connect = true;
            triggerConnectHandler(client);
            modifyEvent(client->fd, EPOLLIN, events[i].data.ptr);
        }else if(events[i].events & EPOLLIN){
            // READER 读取数据
            doRead((long long *)events[i].data.ptr);
        }else if(events[i].events & EPOLLERR) {
            // 链接失败
            closeClient(client);
        }else if(events[i].events & EPOLLHUP) {
            // 有链接被挂断 EPOLLHUP
            closeClient(client);
        }else if (events[i].events & EPOLLRDHUP) {
            // 有链接被挂断 EPOLLRDHUP
            closeClient(client);
        }
    }
}

sockaddr_in *Socket::getServerAddr() {
    return &server_addr;
}

void Socket::setEventHandler(Event * c) {
    eventHandler = c;
}

void Socket::setProtocolConnectHandler(void (*_protocol_connect_handler)(Connection *)) {
    protocol_connect_handler = _protocol_connect_handler;
}

bool Socket::removeConnectionFormList(long long client_id) {
    try{
        connection_list->erase(client_id);
        return true;
    } catch (std::out_of_range) {
        // 没有找到对应的链接
        return false;
    }
}

std::unordered_map<long long, Connection *> *Socket::getConnectionList() {
    return connection_list;
}
/**
 * 检测心跳 超时断开
 */
void Socket::connectionPingInterval() {
    if(this->connection_ping_interval <= 0) {
        return;
    }
    // 创建定时任务
    ping_interval_task = new TimerTask(secondToNanosecond(this->connection_ping_interval), 2, false);
    ping_interval_task->_ptr = this;
    ping_interval_task->setCallFuncPtr([](void * ptr){
        Socket * sock = (Socket *)ptr;
        // 遍历所有链接
        std::unordered_map<long long, Connection *>* connection_list = sock->getConnectionList();
        long now_time = time();
        for(auto iter = connection_list->begin(); iter != connection_list->end();) {
            if(iter->second->fd == iter->second->server_sock->server_fd){
                // 如果是服务端链接跳过
                ++iter;
                continue;
            }
            if((now_time - iter->second->last_session_time) >= sock->connection_ping_interval){
                // 断开链接
                iter->second->server_sock->closeClient(iter);
                connection_list->erase(iter++);
                continue;
            }else{
                ++iter;
            }
        }
    });
    ping_interval_task->setCallFuncDestroyPtr([](void * ptr){
        ptr = NULL;
    });
    Timer::timer->addTimer(ping_interval_task);
}

void Socket::closeClient(std::unordered_map<long long, Connection*>::iterator it) {
    int closed = close(it->second->fd);
    if(closed == -1) {
        perror("关闭链接失败\n");
    }
    deleteEvent(it->second->fd, EPOLLIN);
    triggerCloseHandler(it->second);
    delete it->second;
}

Connection *Socket::getConnectionFormList(long long * client_id) {
    try{
        return connection_list->at(*client_id);
    } catch (std::out_of_range) {
        return NULL;
    }
}

bool Socket::checkClientIdIsExist(long long id) {
    return (connection_list->find(id) == connection_list->end()) ? false : true;
}

int Socket::sendToClient(long long client_id, BUFF * buff) {
    Connection * client = getConnectionFormList(&client_id);
    if(client == NULL) return 0;
    BUFF * sendBuff = client->server_sock->protocol->encode(buff, client);
    int send_len = send(client->fd, sendBuff->buff, sendBuff->buff_len, MSG_DONTWAIT);
    DEL_BUFF(sendBuff);
    return send_len;
}


/**
 * 链接对象构造函数
 * @param _fd 套接字
 * @param _client_addr 链接地址信息
 */
Connection::Connection(int _fd, sockaddr_in _client_addr) {
    fd = _fd;
    client_addr.sin_addr = _client_addr.sin_addr;
    client_addr.sin_port = _client_addr.sin_port;
    client_addr.sin_family = _client_addr.sin_family;
    client_id = Socket::getUnionId(&_client_addr);
    appPtr = NULL;
    conn_buffer = new VariableBuff();
    last_session_time = time();
    is_connect = false;
}

/**
 * 链接对象构造函数 不可被调用
 */
Connection::Connection() {
    printf("该对象不可被自动初始化\n");
    exit(-100);
}

/**
 * 对象字符串表示
 * @return 字符串
 */
char *Connection::toString() {
    char * string = new char[200];
    sprintf(string, "fd = %d, client_ip_int = %d, client_ip = %s, client_port = %d, client_id = %lld\n",
            fd, client_addr.sin_addr.s_addr, inet_ntoa(client_addr.sin_addr), client_addr.sin_port, client_id
            );
    return string;
}

Connection::~Connection() {
    delete conn_buffer;
}

int Connection::sendToCurrentClient(BUFF * buff) {
    BUFF * sendBuff = server_sock->protocol->encode(buff, this);
    int send_len = send(fd, sendBuff->buff, sendBuff->buff_len, MSG_DONTWAIT);
    DEL_BUFF(sendBuff);
    return send_len;
}
